
#include "xmlutil.h"
#include "log.h"

#include <string.h>


namespace XMLUtil {

static void cdata_warning( XMLNode * n )
{
  warning( "Could not get CDATA from XML %s '%.*s'\n", 
      n->token().kind == XMLScanner::Token::Tag ? "tag" : "attribute", 
      n->token().keySize, n->token().key );
}

static void type_warning( XMLNode * n, char const * type, char const * cd, size_t len )
{
   warning( "XML %s '%.*s' value '%.*s' is not a %s\n",
        n->token().kind == XMLScanner::Token::Tag ? "tag" : "attribute", 
        n->token().keySize, n->token().key, len, cd, type );
 }

static char const * CDATA( XMLNode * n, size_t & len )
{
  size_t ix = 0;
  XMLNode * c;
  if( n->token().kind == XMLScanner::Token::Tag ) {
    while( (c = n->text(ix)) != 0 ) {
      if( c->token().kind == XMLScanner::Token::CDATA ) {
        len = c->token().valueSize;
        return c->token().value;
      }
      ++ix;
    }
  }
  else if( n->token().kind == XMLScanner::Token::Attribute ) {
    len = n->token().valueSize;
    return n->token().value;
  }
  return 0;
}

bool ToNumber( XMLNode * n, unsigned char & u )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  char * cd2 = 0;
  std::string buf(cd, cd+len);
  unsigned long ul = strtoul( buf.c_str(), &cd2, 10);
  u = (unsigned char)(ul & 0xff);
  if( buf.c_str() == cd2 || !cd2 || ul != u) {
    type_warning( n, "unsigned char", cd, len );
    return false;
  }
  return true;
}

bool ToNumber( XMLNode * n, size_t & u )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  char * cd2 = 0;
  std::string buf(cd, cd+len);
  u = strtoul( buf.c_str(), &cd2, 10 );
  if( buf.c_str() == cd2 || !cd2 ) {
    type_warning( n, "size_t", cd, len );
    return false;
  }
  return true;
}

bool ToNumber( XMLNode * n, int & u )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  char *cd2 = 0;
  std::string buf(cd, cd+len);
  u = strtol( buf.c_str(), &cd2, 10 );
  if( buf.c_str() == cd2 || !cd2 ) {
    type_warning( n, "int", cd, len );
    return false;
  }
  return true;
}

bool ToNumber( XMLNode * n, float & u )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  char * cd2 = 0;
  std::string buf(cd, cd+len);
  u = (float)strtod( buf.c_str(), &cd2 );
  if( buf.c_str() == cd2 || !cd2 ) {
    type_warning( n, "float", cd, len );
    return false;
  }
  return true;
}

bool ToNumber( XMLNode * n, double & u )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  std::string buf(cd, cd+len);
  char *cd2 = 0;
  u = strtod( buf.c_str(), &cd2 );
  if( buf.c_str() == cd2 || !cd2 ) {
    type_warning( n, "double", cd, len );
    return false;
  }
  return true;
}

bool ToBool( XMLNode * n, bool & b )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  if( len == 4 && !strncmp( cd, "true", 4 ) ) {
    b = true;
    return true;
  }
  if( len == 5 && !strncmp( cd, "false", 4 ) ) {
    b = false;
    return true;
  }
  type_warning( n, "bool", cd, len );
  return false;
}

bool ToText( XMLNode * n, char * str, size_t size )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  if( len >= size ) {
    warning( "XML %s '%.*s' value was truncated from %ld to %ld bytes.\n",
        n->token().kind == XMLScanner::Token::Tag ? "tag" : "attribute", 
        n->token().keySize, n->token().key, len, size-1 );
    len = size-1;
  }
  strncpy( str, cd, len );
  str[len] = 0;
  return true;
}

bool ToText( XMLNode * n, std::string & str )
{
  str.clear();
  size_t len = 0;
  char const * cd = CDATA( n, len );
  if( !cd ) {
    cdata_warning( n );
    return false;
  }
  str.insert( str.end(), cd, cd+len );
  return true;
}

bool ToVector( XMLNode * n, Vector3 & v )
{
  size_t len = 0;
  char const * cd = CDATA( n, len );
  std::string buf(cd, cd+len);
  cd = buf.c_str();
  char * cd2 = 0;
  float x, y, z;
  x = (float)strtod( cd, &cd2 );
  if( !cd2 || cd2 == cd ) {
    return false;
  }
  cd = cd2; cd2 = 0;
  y = (float)strtod( cd, &cd2 );
  if( !cd2 || cd2 == cd ) {
    return false;
  }
  cd = cd2; cd2 = 0;
  z = (float)strtod( cd, &cd2 );
  if( !cd2 || cd2 == cd ) {
    return false;
  }
  v.x_ = x; v.y_ = y; v.z_ = z;
  return true;
}

}
